﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using FourierTools.DSP;
namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Fourier Transformation (Forward, Backward)
    /// </summary>
    public class FourierTransformation : BaseImageFilter
    {
        protected FourierDirection m_FourierDirection = FourierDirection.Forward;

        public FourierTransformation()
            : this(FourierDirection.Forward)
        {
        }

        public FourierTransformation(FourierDirection _fourierDirection)
        {
            m_FourierDirection = _fourierDirection;
        }

        internal FourierTransformation(FourierTransformation _fourierTransformation)
        {
            this.m_FourierDirection = _fourierTransformation.m_FourierDirection;
        }

        public FourierDirection Direction
        {
            get { return m_FourierDirection; }
            set { m_FourierDirection = value; }
        }

        //TODO: write pic back into buffer.
        /// <summary>
        /// It works correctly, but now what? How to write the pic back!?
        /// </summary>
        /// <param name="_bitmap"></param>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeFourierBitmap bitmap = new UnsafeFourierBitmap(_bitmap);
            
            int nWidth = bitmap.Width;
            int nHeight = bitmap.Height;

            float scale = 1f / (float)Math.Sqrt(nWidth * nHeight);

            int nOffset = 0;
            int y = 0;
            int x = 0;
            int i = 0;

            switch (m_FourierDirection)
            {
                case FourierDirection.Forward:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            if (((x + y) & 0x1) != 0)
                            {
                                bitmap.Data[nOffset] *= -1;
                            }
                            nOffset++;
                        }
                    }

                    Fourier.FFT2(bitmap.Data, bitmap.Width, bitmap.Height, FourierDirection.Forward);

                    bitmap.FrequencySpace = true;

                    for (i = 0; i < bitmap.Data.Length; i++)
                    {
                        bitmap.Data[i] *= scale;
                    }
                    break;

                case FourierDirection.Backward:

                    Fourier.FFT2(bitmap.Data, bitmap.Width, bitmap.Height, FourierDirection.Backward);

                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x <  nWidth; x++)
                        {
                            if (((x + y) & 0x1) != 0)
                            {
                                bitmap.Data[nOffset] *= -1;
                            }
                            nOffset++;
                        }
                    }

                    bitmap.FrequencySpace = false;

                    for (i = 0; i < bitmap.Data.Length; i++)
                    {
                        bitmap.Data[i] *= scale;
                    }
                    break;
            }
            return _bitmap;
        }

        public unsafe Bitmap ToBitmap(Bitmap _bitmap, ComplexF[] _data)
        {
            Rectangle rect = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
            BitmapData bitmapData = _bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
            int* colorData = (int*)bitmapData.Scan0.ToPointer();

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            for (int i = 0; i < nWidth * nHeight; i++)
            {
                int c = Math.Min(255, Math.Max(0, (int)(256 * _data[i].GetModulus())));
                colorData[i] = Color.FromArgb(c, c, c).ToArgb();
            }
            _bitmap.UnlockBits(bitmapData);
            return _bitmap;
        }

        /// <summary>
        /// Clones this instance
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new FourierTransformation(this);
        }
    }
}
